/***
 * * PROJECT
 * *    Logical Neighborhood Routing
 * * VERSION
 * *    $Revision: 1.3 $
 * * DATE
 * *    $Date: 2007/06/01 20:52:31 $
 * * LOG
 * *    $Log: LNSwansEstimator.java,v $
 * *    Revision 1.3  2007/06/01 20:52:31  animesh
 * *    bulk update.
 * *
 * *    Revision 1.2  2007/01/04 09:22:42  lmottola
 * *    Changed getEstimation signature in Estimator interface.
 * *
 * *    Revision 1.1  2006/12/20 23:05:50  lmottola
 * *    SWANS-based simulator in.
 * *
 * *
 * *   Logical Neighborhoods Routing - Java Version
 * *
 * *   This program is free software; you can redistribute it and/or
 * *   modify it under the terms of the GNU General Public License
 * *   as published by the Free Software Foundation; either version 2
 * *   of the License, or (at your option) any later version.
 * *
 * *   This program is distributed in the hope that it will be useful,
 * *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 * *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * *   GNU General Public License for more details.
 * *
 * *   You should have received a copy of the GNU General Public License
 * *   along with this program; if not, you may find a copy at the FSF web
 * *   site at 'www.gnu.org' or 'www.fsf.org', or you may write to the
 * *   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * *   Boston, MA  02111-1307, USA
 ***/

package atag.runtime.ln.estimation;

import java.util.Iterator;
import java.util.List;

import jist.runtime.JistAPI;
import jist.swans.Constants;
import jist.swans.app.AppJava;
import jist.swans.field.Field;
import jist.swans.field.Mobility;
import jist.swans.mac.MacAddress;
import jist.swans.mac.MacDumb;
import jist.swans.misc.Location;
import jist.swans.misc.Mapper;
import jist.swans.misc.Util;
import jist.swans.net.NetAddress;
import jist.swans.net.NetIp;
import jist.swans.net.PacketLoss;
import jist.swans.radio.RadioInfo;
import jist.swans.radio.RadioNoise;
import jist.swans.radio.RadioNoiseIndep;
import jist.swans.trans.TransUdp;
import atag.common.ATaGProgram;
import atag.common.NetworkTopology;
import atag.compiler.Estimator;
import atag.runtime.AtagManager;
import atag.runtime.DataPool;
import atag.runtime.NodeInfo;
import atag.runtime.ln.DatagramObjectIO;
import atag.runtime.ln.LogicalNeighborhood;
import atag.runtime.ln.swans.SimulationReferences;

public class LNSwansEstimator implements Estimator {

	private NetworkTopology topology;

	private Class atagManagerClass;

	private ATaGProgram prg;

	private long simTime;

	private long spreadTime;

	/**
	 * Creates a new estimator based on SWANS simulation. The ATaG manager and
	 * program must be provided at this time, along with the timing aspects for
	 * the simulation itself, e.g., the total simulation time. Also, the
	 * NetworkTopology must contain a collection of NodeInfo objects as created
	 * on a real system, i.e., including the clock and list of assigned tasks.
	 * 
	 * @param topology
	 *            the network topology to simulate
	 * @param atagManager
	 *            the AtagManager to be used in the simulated application
	 * @param warmUpTime
	 *            how much (simulated) time the application is run before
	 *            gathering the actual statistics
	 * @param simTime
	 *            the total simulation time
	 * @param spreadTime
	 *            the time interval to use between the startup of two simulated
	 *            nodes
	 */
	public LNSwansEstimator(NetworkTopology topology, Class atagManagerClass,
			ATaGProgram program, long simTime, long spreadTime) {

		this.topology = topology;
		this.prg = program;
		this.atagManagerClass = atagManagerClass;
		this.simTime = simTime;
		this.spreadTime = spreadTime;
	}

	public double[] getCostEstimate(List commPatterns) {

		// Building the simulation field
		Location.Location2D bounds = new Location.Location2D(topology
				.getXRange(), topology.getYRange());
		Mobility mobility = new Mobility.Static();
		Field field = new Field(bounds);
		field.setMobility(mobility);
		RadioInfo.RadioInfoShared radioInfoShared = RadioInfo.createShared(
				Constants.FREQUENCY_DEFAULT, Constants.BANDWIDTH_DEFAULT,
				Constants.TRANSMIT_DEFAULT, Constants.GAIN_DEFAULT, Util
						.fromDB(Constants.SENSITIVITY_DEFAULT), Util
						.fromDB(Constants.THRESHOLD_DEFAULT),
				Constants.TEMPERATURE_DEFAULT,
				Constants.TEMPERATURE_FACTOR_DEFAULT,
				Constants.AMBIENT_NOISE_DEFAULT);
		// protocol mapper
		Mapper protMap = new Mapper(Constants.NET_PROTOCOL_MAX);
		protMap.mapToNext(Constants.NET_PROTOCOL_UDP);

		// packet loss
		PacketLoss pl = new PacketLoss.Zero();
		
		// Setting simulation end time
		//JistAPI.endAt(simTime);

		// Creating and starting the nodes
		Iterator itNodes = topology.getNodes().iterator();
		System.out.println("Nodes: " + topology.getNodes().size());
		while (itNodes.hasNext()) {
			NodeInfo node = (NodeInfo) itNodes.next();
			System.out.println("Starting node " + node.getMyId());
			try {
				createNode(field, radioInfoShared, protMap, pl, pl, node, prg);
				Thread.sleep(spreadTime);
			} catch (NoSuchMethodException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		// TODO: Parsing output file and return results

		return new double[0];
	}

	private void createNode(Field field,
			RadioInfo.RadioInfoShared radioInfoShared, Mapper protMap,
			PacketLoss plIn, PacketLoss plOut, NodeInfo info, ATaGProgram prg)
			throws NoSuchMethodException {

		int id = info.getMyId();

		// Create entities
		RadioNoise radio = new RadioNoiseIndep(id, radioInfoShared);
		MacDumb mac = new MacDumb(new MacAddress(id), radio.getRadioInfo());
		NetAddress netAddr = NetAddress.LOCAL;
		NetIp net = new NetIp(netAddr, protMap, plIn, plOut);
		TransUdp udp = new TransUdp();

		// hookup entities
		Location location = new Location.Location2D(
				info.getMyLocation().getX(), info.getMyLocation().getY());
		field.addRadio(radio.getRadioInfo(), radio.getProxy(), location);
		field.startMobility(radio.getRadioInfo().getUnique().getID());

		// radio hookup
		radio.setFieldEntity(field.getProxy());
		radio.setMacEntity(mac.getProxy());

		// mac hookup
		mac.setRadioEntity(radio.getProxy());
		byte intId = net.addInterface(mac.getProxy());
		mac.setNetEntity(net.getProxy(), intId);

		// net hookup
		net.setProtocolHandler(Constants.NET_PROTOCOL_UDP, udp.getProxy());

		// trans hookup
		udp.setNetEntity(net.getProxy());

		SimulationReferences.setNodeInfo(id, info);

		DatagramObjectIO objectIO = new DatagramObjectIO(
				LogicalNeighborhood.PORT, LogicalNeighborhood.BUFFER_SIZE,
				LogicalNeighborhood.RECEIVE_TIMEOUT);
		SimulationReferences.setObjectIO(id, objectIO);
		AppJava runObjectIO = new AppJava(DatagramObjectIO.class);
		runObjectIO.setUdpEntity(udp.getProxy());
		runObjectIO.getProxy().run(new String[] { String.valueOf(id) });
		JistAPI.sleep(1);

		LogicalNeighborhood ln = new LogicalNeighborhood(info, objectIO);
		SimulationReferences.setLN(id, ln);
		AppJava runLNTick = new AppJava(LogicalNeighborhood.class);
		runLNTick.setUdpEntity(udp.getProxy());
		runLNTick.getProxy().run(
				new String[] { "tickStart", String.valueOf(id) });
		JistAPI.sleep(1);
		AppJava runLNQueue = new AppJava(LogicalNeighborhood.class);
		runLNQueue.setUdpEntity(udp.getProxy());
		runLNQueue.getProxy().run(
				new String[] { "queueStart", String.valueOf(id) });
		JistAPI.sleep(1);

		AtagManager managerInstance = null;
		try {
			managerInstance = (AtagManager) atagManagerClass.newInstance();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		managerInstance.setAtagProgram(prg);
		managerInstance.setNodeInfo(info);
		managerInstance.setDataPool(new DataPool(ln, info, managerInstance));
		managerInstance.setUp();
		SimulationReferences.setAtagManager(id, managerInstance);

		AppJava runAtagManager = new AppJava(AtagManager.class);
		runAtagManager.setUdpEntity(udp.getProxy());
		for (int i = 0; i < managerInstance.getAtagPrg().numTasks(); i++) {
			JistAPI.sleep(1);
			runAtagManager.getProxy().run(
					new String[] { String.valueOf(id), String.valueOf(i) });
		}
	}
}
